package F21AS_Freddy.AFJ_Taxikiosk.src;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Random;
import java.util.Scanner;

/**
 * This passenger queue class used to define queuing passenger
 * @author Freddy Wong
 * F21AS Course Work Stage2 @ Year 2015
 */

public class Queue extends Observable implements Iterable<Passenger>, Runnable {

	private Boolean isOpen;
	private String inputFile;
	private LinkedList<Passenger> queue = new LinkedList<Passenger>();
	private String queueStatus;

	// random multiplier which defines the speed at which the queue fills up
	private int queueSpeed = 2500;

	/**
	 * Constructs an empty Queue with a status of 'idle'
	 */
	public Queue(String inputFile) {
		this.inputFile = inputFile;
		isOpen = true;
		queueStatus = "Queue idle";
	}

	/**
	 * Add passenger into the queue
	 * @param passenger
	 * @return
	 * @throws DuplicatePassengerException
	 */
	public boolean addQueue(Passenger passenger) throws DuplicatePassengerException {
		boolean result = false;

		if (passenger == null) {
			throw new IllegalArgumentException();
		} else if (queue.contains(passenger)) {
			throw new DuplicatePassengerException(passenger);
		}

		else {
			synchronized (this) {
				queue.add(passenger);
				queueStatus = ">> Adding passenger " + passenger.getBookingName();
				
				setChanged();
				notifyObservers();

				result = true;

			}
		}
		return result;
	} // End of addQueue Class 

	/**
	 * Verify empty queue
	 * @return
	 */
	public boolean getIsEmpty() {
		return queue.isEmpty();
	}

	public String getQueueStatus() {
		return queueStatus;
	}

	public synchronized void setStatus(String status) {
		this.queueStatus = status;
		
		setChanged();
		notifyObservers();
	}

	public boolean getIsOpen() {
		return isOpen;
	}

	public void setIsOpen(Boolean isOpen) {
		if (this.isOpen != isOpen) {
			this.isOpen = isOpen;
			
			setChanged();
			notifyObservers();
		}

	}

	/**
	 * @param passenger
	 * @return true if the queue contains the passenger passed to this method
	 */
	public boolean contains(Passenger passenger) {
		return (queue.contains(passenger));
	}


	public int startQueue() {

		int numberOfSuccessfulImports = 0;
		int lineNo = 0;

		try {
			Random random = new Random();
			// open the CSV file containing Passenger and Baggage information
			File file;
			file = new File(inputFile);

			// create a Scanner to iterate through teach line of the CSV file
			Scanner fileScanner;
			fileScanner = new Scanner(file);

			// iterate through the file
			while (fileScanner.hasNextLine() && isOpen) {
				lineNo++;

				// read each line in turn
				String inputLine = fileScanner.nextLine();

				// separate name and booking ref elements
				String inputElements[] = inputLine.split(",");

				try {
					// instantiate passenger object
					Passenger p = new Passenger(inputElements[0],inputElements[1],Integer.parseInt(inputElements[2]),Integer.parseInt(inputElements[3]));

					addQueue(p);

					numberOfSuccessfulImports++;

				} catch (IllegalStateException ise) {

					String error = "Error parsing line " + lineNo + " containing " + inputLine + ". Error: " + ise.getMessage();
					System.out.println(error);

				} catch (DuplicatePassengerException dpe) {
					
					String error = "Duplicate Passenger in " + lineNo + " containing " + inputLine + ". Error: " + dpe.getMessage();
					System.out.println(error);

				} catch (Exception e) {
					System.out.println(e.getMessage());

				}

				// pause for a random time between 0 and QUEUE_SPEED ms
				Thread.sleep(random.nextInt(queueSpeed));

			}
			// tidy the file scanner up
			fileScanner.close();

			this.setStatus("Queue building complete, "
					+ numberOfSuccessfulImports + " passengers added.");

		} catch (FileNotFoundException fnf) {
			String error = "Failed to parse file" + inputFile + ". Error: " +fnf.getMessage();
			System.out.println(error);

		} catch (Exception e) {
			String error = "Error: " +e.getMessage();
			System.out.println(error);
		} 
		
		return numberOfSuccessfulImports;
	}

	/**
	 * 
	 * @return - Returns the first passenger in the queue. 
	 * The passenger object is removed from the queue
	 */
	public synchronized Passenger removeFirst() {
		try {
			Passenger passenger = queue.removeFirst();
			queueStatus = "<< Removing passenger " + passenger.getBookingName();
			
			setChanged();
			notifyObservers();

			return passenger;

		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 
	 * @return - Returns the first passenger in the queue. 
	 * The passenger object remains in the queue
	 */
	public synchronized Passenger getFirst() {
		try {
			Passenger passenger = queue.getFirst();

			return passenger;

		} catch (Exception e) {
			return null;
		}
	}

	public synchronized void remove(Passenger passenger) {

		try {
			queue.remove();
			queueStatus = "<< Removing passenger " + passenger.getBookingName();
			
			setChanged();
			notifyObservers();

		} catch (Exception e) {
				e.getStackTrace();
		}

	}

	/**
	 * 
	 * @return an iterator of Passenger objects
	 */
	@Override
	public Iterator<Passenger> iterator() {
		return queue.iterator();
	}

	/**
	 * 
	 * @return number of passengers in the queue
	 */
	public int size() {
		return queue.size();
	}

	/**
	 * removes all passengers from the queue
	 */
	public void clear() {
		queue.clear();
		
		setChanged();
		notifyObservers();
	}

	/**
	 * 
	 * @param passenger
	 * - passenger to be found in queue
	 * @return returns integer specifying the location of the passenger in the queue. 
	 * Returns -1 if passenger not in the queue
	 */
	public int indexOf(Passenger passenger) {
		return queue.indexOf(passenger);
	}

	@Override
	public void run() {
		this.startQueue();
	}

	/**
	 * Set speed of the queue with the production of random number
	 * @param queueSpeed
	 */
	public void setQueueSpeed(int queueSpeed) {
		this.queueSpeed = queueSpeed;
	}

} //End Of Queue Class